(defpackage translator
  (:use :cl))
(in-package :translator)

(defparameter *app-key* nil)

(defparameter *app-secret* nil)

(defvar *url* (format nil "https://openapi.youdao.com/api"))

(defparameter *input-value* nil)

(defstruct dict q from to appkey salt sign signtype curtime)

(defun sha256-hex (input)
  (ironclad:byte-array-to-hex-string (ironclad:digest-sequence :sha256 (babel:string-to-octets input :encoding :utf-8))))

(defun salt (now-time)
  (let ((now-sec (* (local-time:timestamp-to-unix now-time) 1000))
	(now-milli (local-time:timestamp-millisecond now-time)))
    (+ now-sec now-milli)))

(defun truncate-input (input)
  (let ((len (length input)))
    (if (> len 20)
	(let ((head (subseq input 0 10))
	      (end (subseq input (- len 10) len)))
	  (format nil "~a~a~a" head (write-to-string len) end))
	input)))

(defun sign (input)
  (let* ((time-now (salt (local-time:now)))
	 (salt (write-to-string time-now))
	 (curtime (write-to-string (round (/ time-now 1000))))
	 (truncate-str (truncate-input input))
	 (str (concatenate 'string *app-key* truncate-str salt curtime *app-secret*)))
    (list salt curtime (sha256-hex str))))

(defun get-from (word)
  (if (string-equal (cl-ppcre:scan-to-strings "[a-zA-Z]*" word) "")
      "zh-CHS"
      "en"))

(defun get-to (from)
  (if (string-equal from "en")
      "zh-CHS"
      "en"))

(defun translate (word)
  (let* ((from (get-from word))
	 (to (get-to from))
	 (v (sign word)))
    (dex:post *url*
	      :content `(("q" . ,word)
			 ("from" . ,from)
			 ("to" . ,to)
			 ("appKey" . ,*app-key*)
			 ("salt" . ,(first v))
			 ("sign" . ,(third v))
			 ("signType" . "v3")
			 ("curtime" . ,(second v))))))

(defun read-result (result)
  (let* ((body (com.inuoe.jzon:parse result))
	(translation (gethash "translation" body)))
    (aref translation 0)))

(defun do-translate (input)
  (format t "~a~%" (read-result (translate input))))


(defun read-input ()
  (format *query-io* "translator> ")
  (force-output *query-io*)
  (read-line *query-io*))

(defun init()
  (let ((config (init-config)))
    (setf *app-key* (appinfo-key config))
    (setf *app-secret* (appinfo-secret config))))

(defun main ()
  (init)
  (loop
    (let ((input (read-input)))
      (if (string-equal input ":exit")
	  (return)
	  (do-translate input)))))
